(*
     The contents of this file are subject to the Mozilla Public License
     Version 1.1 (the "License"); you may not use this file except in
     compliance with the License. You may obtain a copy of the License at
     http://www.mozilla.org/MPL/

     Software distributed under the License is distributed on an "AS IS"
     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
     License for the specific language governing rights and limitations
     under the License.

     The Original Code is CrossFPC.

     The Initial Developer of the Original Code is Simon Kissel.
     Portions created by Benjamin Rosseaux are Copyright (C) Benjamin Rosseaux.
     All Rights Reserved.

*)

{

0.4.5   19/09/2015

- Updated FPC builds to fixes_3_0 SVN revision 31772
- Rewrote ReadAndConvertProjectCfg as the code used until now produced broken paths
- Cleaned up source code tree and removed all the duplicates, and moved this
  code to ../common
  
0.4.4   23/02/2015

- Updated FPC builds to SVN revision 28288
- Added CrossProfiler unit
- Added unfinished and untested Delphi XE5 support
- Fixed build options
- Fixed standalone install issue in the installer, when no Delphi version was installed
- Added units
- Added experimental Delphi XE6 and Delphi XE7 support
 
0.4.3   25/08/2013

- Updated FPC builds to SVN revision 25438
- Added ARMv7a softfloat linux target
- Fixed path overwrite issue in the installer
- Fixed possible crash issue in the installer 

0.4.2   23/04/2013

- Added Delphi XE4 support

0.4.1   13/02/2013

- Fixed non-english Delphi installation bugs in the installer
- Added program-group-create-feature to the installer
- Added android ARM target
- Fixed and reenabled x86-64 linux target
- Verified INI code on the IDE plugin side for correct target handling
- Updated all FPC binaries and units to revision 23583 (and the android ARM target to revision 23607)
- Updated BRRE and BESEN externals

0.4.0   30/12/2012

- License header added

0.3.6   02/12/2012

- Added compatibility for Delphi 2007, 2009, 2010, XE, XE2, XE3

0.3.3   03/10/2005

- Updated fpc

0.3.2   25/09/2005

- Small visual changes
- removed unneeded cfresfix unit

0.3.1   24/09/2005

- Adapted to newest fpc build
- Default unit search path should now be correctly generated

0.3.0   17/09/2005

- Lot's of changes in the framework etc
- Compiler options dialog now should be working

0.2.1   07/05/2005

- Fixed default encoding codepage, which doesn't work for FPC (CrossKylix artifact)
- Added missing files to the distribution ZIP
- Verbose switch now only is added to the fpc commandline if debug message display is turned on

0.2.0   06/05/2005

Second internal version. Here are the main changes:
- Added compiler options dialog done by Michael Van Canneyt (thanks!)
- Now uses a new LD linker version, fixing lots of issues (thanks to oliebol)
- Creates dynlinked applications that seem to run on all linux distros. If NPTL is available, it is used.
- Fixed an issue that caused problems if the project directory wasn't your current dir in the IDE


0.1.0   28/02/2005
- First internal version, based on my CrossKylix IDE plugin

}

{$WARN UNIT_PLATFORM OFF}
// because of FileCtrl usage

unit cfmain;

interface

{$ifndef standalonecrossfpc}
procedure Register;
procedure dolog(const msg: string; refresh:boolean=true);

implementation
{$endif}
uses
  BESEN, BESENValue, BESENTypes, BESENConstants, BESENNativeObject, BESENObject, BESENUtils,
  BESENStringUtils, BESENObjectFunction, BESENErrors,
  cfbesenobjects,
  BeRoAppDataPath,
  {$ifndef standalonecrossfpc}ToolsAPI,{$endif} Dialogs, Windows, menus, SysUtils, winsock, classes, controls, forms, ComCtrls,
  {$ifdef standalonecrossfpc}StdCtrls, Messages,{$endif}
  Graphics, inifiles, FileCtrl, cfcompiledialog, cfoptionsdialog, cfCompilerOptions,
  cfpcutils, cfpdprconvert;

type
  TCrossFPCAddIn = class; // forward

{$ifdef standalonecrossfpc}
  IOTAModule=pointer;
  IOTAProject=pointer;
{$endif}

{$ifndef standalonecrossfpc}
  TCrossFPCNotifier = class(TNotifierObject, IOTANotifier, IOTAIDENotifier)
  private
  protected
    procedure FileNotification(NotifyCode: TOTAFileNotification;
      const FileName: string; var Cancel: Boolean);
    procedure BeforeCompile(const Project: IOTAProject; var Cancel: Boolean); overload;
    procedure AfterCompile(Succeeded: Boolean); overload;
  public
    CrossFPCAddIn: TCrossFPCAddIn;
  end;
{$endif}

  TCrossFPCTarget = record
    ShortName: string;
    FriendlyName: string;
    CommandLine: string;
    Parameters: string;
    UtilDir: string;
    MenuItem: TMenuItem;
  end;

  TCrossFPCAddIn = class{$ifndef standalonecrossfpc}(TNotifierObject, IOTAWIzard){$endif}
  private
    ProjectMenu: TMenuItem;
    ToolsMenu: TMenuItem;
    CompileMenuItem: TMenuItem;
    FPCTargetMenuItem: TMenuItem;
    BuildMenuItem: TMenuItem;
    DebugMenuItem: TMenuItem;
    SeparatorMenuItem: TMenuItem;
    ToolsMenuItem: TMenuItem;
    cd: TCompileDialog;
    od: TOptionsDialog;
    ini: TMemInifile;
    BaseDir : String;
    Options : TDirectoryOptions;
    ProjectOptions: TProjectOptions;
    Encoding: string;
    DebugOutput: boolean;
//    DebugTrace: boolean;
    Line: string;
    projectpath: string;
    currentdir: string;
    w_count, e_count, h_count, l_count: integer;
    currentfile: string;
    cc: integer;
{$ifndef standalonecrossfpc}
    CrossFPCNotifierIndex: integer;
    TargetCount: Integer;
    FPCTargets: array of TCrossFPCTarget;
    FPCTarget: Integer;
{$endif}
    BESENInstance:TBESEN;

{$ifndef standalonecrossfpc}
    function GetIDString: string;
    function GetName: string;
    function GetState: TWizardState;
{$endif}
{$ifndef standalonecrossfpc}
    procedure execute;
{$endif}
    procedure docompile(build: boolean);
//    procedure msgoutput(const s: string);
//    procedure CancelCompile(Sender: TObject);
  public
{$ifdef standalonecrossfpc}
    TargetCount: Integer;
    FPCTargets: array of TCrossFPCTarget;
    FPCTarget: Integer;
{$endif}
    procedure debugFPC(Sender: TObject);
    procedure buildFPC(Sender: TObject);
    procedure compileFPC(Sender: TObject);
    procedure showoptions(Sender: TObject);
    procedure SetTarget(Sender: TObject);
    procedure CheckProjectFile;

    Procedure LoadOptions;
    Procedure SaveOptions;
    constructor Create;
    destructor Destroy; override;
  end;

{$ifdef standalonecrossfpc}
var CurrentProject : byte;
    CurrentMainFileName : string;
    CrossFPCAddIn: TCrossFPCAddIn;
    MessagesMemo: TMemo;

procedure Register;
procedure dolog(const msg: string; refresh:boolean=true);

implementation

function GetMainFilename(module: IOTAModule):string;
begin
  result := CurrentMainFileName;
end;

function GetCurrentProject: IOTAProject;
begin
  result := @CurrentProject;
end;
{$else}
function GetMainFilename(module: IOTAModule):string;
var i: integer;
    editor: IOTAEditor;
    fname: string;
    ext: string;
begin
  result:='';
  for i:=0 to module.ModuleFileCount-1 do
  begin
    editor:=module.ModuleFileEditors[i];
    if assigned(editor) then
    begin
      fname:=editor.FileName;
      ext:=lowercase(ExtractFileExt(fname));
      if (ext='.dpr') or (ext='.dpk') then
      begin
        result:=fname;
      end;
    end;
  end;
end;

function GetCurrentProject: IOTAProject;
var
  Services: IOTAModuleServices;
  Module: IOTAModule;
  Project: IOTAProject;
  ProjectGroup: IOTAProjectGroup;
  MultipleProjects: Boolean;
  I: Integer;
begin
  Result := nil;
  MultipleProjects := False;
  Services := BorlandIDEServices as IOTAModuleServices;
  for I := 0 to Services.ModuleCount - 1 do
  begin
    Module := Services.Modules[I];
    if Module.QueryInterface(IOTAProjectGroup, ProjectGroup) = S_OK then
    begin
      Result := ProjectGroup.ActiveProject;
      Exit;
    end
    else if Module.QueryInterface(IOTAProject, Project) = S_OK then
    begin
      if Result = nil then
        // Found the first project, so save it
        Result := Project
      else
        MultipleProjects := True;
      // It doesn't look good, but keep searching for a project group
    end;
  end;
  if MultipleProjects then
    Result := nil;
end;
{$endif}

function FindClassForm(const AClassName: string): TForm;
var
  I: Integer;
begin
  Result := nil;
  with Screen do
    for I := 0 to FormCount - 1 do
      if Forms[I].ClassNameIs(AClassName) then
      begin
        Result := Forms[I];
        Break;
      end;
end;

function FindMessageViewForm: TForm;
begin
  // if TMsgWindow exists all messages are sent to it
  Result := FindClassForm('TMsgWindow');
  if not Assigned(Result) then // otherwise TMessageViewForm is used
    Result := FindClassForm('TMessageViewForm');
end;

procedure ShowMessageView(ScrollToLast: Boolean = False);
var
  MessageViewForm: TForm;
  I: Integer;
  TreeView: TTreeView;
  Node: TTreeNode;
begin
  if csDestroying in Application.ComponentState then
    Exit;

  MessageViewForm := FindMessageViewForm;
  if Assigned(MessageViewForm) then
  begin
    if ScrollToLast then
    begin
      TreeView := nil;
      with MessageViewForm do
        for I := 0 to ControlCount - 1 do
          if Controls[I].ClassNameIs('TTreeMessageView') then
          begin
            TreeView := Controls[I] as TTreeView;
            Break;
          end;

      if Assigned(TreeView) then
      begin
        Node := TreeView.Items.GetFirstNode;
        while Assigned(Node) and (Node.GetNextSibling <> nil) do
          Node := Node.GetNextSibling;
        if Assigned(Node) then
          Node.MakeVisible;
      end;
    end;

    MessageViewForm.Show;
  end;
end;

function PathGetShortName(const APath: string): string;
var
  Buffer: array[0..MAX_PATH] of Char;
  Required: Integer;
begin
  Required := GetShortPathName(PChar(APath), Buffer, SizeOf(Buffer));
  if Required > MAX_PATH then // Buffer to small
  begin
    SetLength(Result, Required - 1);
    GetShortPathName(PChar(APath), Pointer(Result), Required);
  end
  else if Required = 0 then // Error
    Result := APath
  else
    SetString(Result, Buffer, Required);
end;

{$ifndef standalonecrossfpc}
function TCrossFPCAddIn.GetIDString: string;
begin
  Result := 'scamp.CrossFPC';
end;

function TCrossFPCAddIn.GetName: string;
begin
  Result := 'CrossFPC';
end;

function TCrossFPCAddIn.GetState: TWizardState;
begin
  Result := [wsEnabled];
end;
{$endif}

procedure Register;
begin
{$ifndef standalonecrossfpc}
  RegisterPackageWizard(TCrossFPCAddIn.Create as IOTAWizard)
{$endif}
end;

constructor TCrossFPCAddIn.Create;
{$ifndef standalonecrossfpc}
var
  i: integer;
  CrossFPCNotifier: TCrossFPCNotifier;
{$endif}
begin
  inherited;

  ini := TMemInifile.create(IncludeTrailingPathDelimiter(String(GetAppDataPath('CrossFPC'))) + 'CrossFPC.ini');
//  Options:=TCompilerOptions.Create;
  ProjectOptions:=TProjectOptions.Create;
  LoadOptions;

  FPCTarget:=0;
  ProjectMenu := nil;
  ToolsMenu := nil;
  SeparatorMenuItem := nil;
  CompileMenuItem := nil;
  BuildMenuItem := nil;
  DebugMenuItem := nil;
  ToolsMenuItem := nil;
  FPCTargetMenuItem := nil;

{$ifndef standalonecrossfpc}
  with TMenuItem((BorlandIDEServices as INTAServices).MainMenu.items) do
  begin
    for i := 0 to count - 1 do
    begin
      if items[i].name = 'ProjectMenu' then
      begin
        ProjectMenu := items[i];
      end
      else if items[i].name = 'ToolsMenu' then
      begin
        ToolsMenu := items[i];
      end;
    end;
  end;

  if assigned(ProjectMenu) then
  begin
    SeparatorMenuItem := TMenuItem.Create(projectMenu);
    SeparatorMenuItem.Caption := '-';
    projectMenu.Add(SeparatorMenuItem);
    FPCTargetMenuItem := TMenuItem.Create(projectMenu);
    FPCTargetMenuItem.Caption := 'CrossFPC Target';
    projectMenu.Add(FPCTargetMenuItem);

    for i:=0 to TargetCount-1 do
    with FPCTargets[i] do
    begin
      MenuItem := TMenuItem.Create(FPCTargetMenuItem);
      MenuItem.Caption := FriendlyName;
      MenuItem.Tag := i;
      MenuITem.OnClick := SetTarget;
      if i = FPCTarget then
      MenuItem.Checked:=True;
      FPCTargetMenuItem.Add(MenuItem);
    end;

    CompileMenuItem := TMenuItem.Create(projectMenu);
    CompileMenuItem.Caption := 'Compile with CrossFPC';
    CompileMenuItem.OnClick := compileFPC;
    projectMenu.Add(CompileMenuItem);

    BuildMenuItem := TMenuItem.Create(projectMenu);
    BuildMenuItem.Caption := 'Build with CrossFPC';
    BuildMenuItem.OnClick := buildFPC;
    projectMenu.Add(BuildMenuItem);

    DebugMenuItem := TMenuItem.Create(projectMenu);
    DebugMenuItem.Caption := 'Debug with CrossFPC Debugger';
    DebugMenuItem.OnClick := debugFPC;
    projectMenu.Add(DebugMenuItem);

  end;
  if assigned(ToolsMenu) then
  begin
    ToolsMenuItem := TMenuItem.Create(ToolsMenu);
    ToolsMenuItem.Caption := 'CrossFPC Options...';
    ToolsMenuItem.OnClick := showoptions;
    ToolsMenu.Insert(4, ToolsMenuItem);
  end;

  CrossFPCNotifier:=TCrossFPCNotifier.Create;
  CrossFPCNotifier.CrossFPCAddIn:=self;
  CrossFPCNotifierIndex:=(BorlandIDEServices as IOTAServices).AddNotifier(CrossFPCNotifier);
{$endif}

  cd := TCompileDialog.Create(application.mainform);
  od := TOptionsDialog.Create(application.mainform);

  basedir := ini.ReadString('CrossFPC', 'BaseDir', '');
  if basedir <> '' then basedir := IncludeTrailingPathDelimiter(basedir);
  if pos(' ', basedir) > 0 then
  begin
    basedir := PathGetShortName(basedir);
  end;

  if not fileexists(basedir + 'bin\i386-win32\fpc.exe') then
  begin
    showoptions(self);
    with Options do
    if UnitDir='' then
    UnitDir:= basedir+'units\$FPCTARGET\*;'+basedir+'lib\$FPCTARGET;'+basedir+'\CommonWin\*';
  end;
end;

destructor TCrossFPCAddIn.Destroy;
{$ifndef standalonecrossfpc}
var i: Integer;
{$endif}
begin
  //  ProjectMenu.Remove(CompileMenuItem);
  //  ProjectMenu.Remove(BuildMenuItem);
  try
{$ifndef standalonecrossfpc}
    (BorlandIDEServices as IOTAServices).RemoveNotifier(CrossFPCNotifierIndex);
    if assigned(CompileMenuItem) then freeandnil(CompileMenuItem);
    if assigned(BuildMenuItem) then freeandnil(BuildMenuItem);
    if assigned(DebugMenuItem) then freeandnil(DebugMenuItem);
    if assigned(ToolsMenuItem) then freeandnil(ToolsMenuItem);
    if assigned(SeparatorMenuItem) then freeandnil(SeparatorMenuItem);
    for i:=0 to TargetCount-1 do
    if assigned(FPCTargets[i].MenuItem) then freeandnil(FPCTargets[i].MenuItem);
    if assigned(FPCTargetMenuItem) then freeandnil(FPCTargetMenuItem);
{$endif}
    cd.Close;
    od.Close;
    freeandnil(cd);
    freeandnil(od);
//    FreeAndNil(Options);
    FreeAndNil(ini);
    FreeAndNil(ProjectOptions);
  finally
    inherited;
  end;
end;

procedure TCrossFPCAddIn.debugFPC(Sender: TObject);
var currentproject: IOTAProject;
    projectname, DebuggerArguments: ansistring;
begin
  if assigned(ProjectOptions) then
  begin
    CheckProjectFile;

    currentdir := getcurrentdir;
    currentproject := GetCurrentProject;
    if assigned(currentproject) then
    begin

      projectname := AnsiString(GetMainFilename(currentproject));

      projectpath := extractfilepath(String(projectname));
      if pos(' ', projectpath) > 0 then
      begin
        projectpath := PathGetShortName(projectpath);
      end;

      projectname := AnsiString(extractfilename(String(projectname)));

      DebuggerArguments := AnsiString(StringReplace( String(ProjectOptions.DebuggerArguments), '{binary}', String(ChangeFileExt(string(projectpath)+string(projectname),'.exe')), [rfReplaceAll, rfIgnoreCase]));

      DoExecute(IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(basedir) + 'debugger') + 'CrossFPCDebugger.exe', String(DebuggerArguments), SW_SHOWNORMAL);
    end;
  end;
end;

procedure TCrossFPCAddIn.buildFPC(Sender: TObject);
begin
  docompile(true);
end;

procedure TCrossFPCAddIn.compileFPC(Sender: TObject);
begin
  docompile(false);
end;

{$ifndef standalonecrossfpc}
procedure TCrossFPCAddIn.execute;
begin
  //
end;
{$endif}

function getlinuxpath(s: string): string;
var
  i, j: integer;
  sl: TStringList;
begin
  sl := TStringList.Create;

  while pos(';', s) > 0 do
  begin
    sl.add(copy(s, 1, pos(';', s) - 1));
    delete(s, 1, pos(';', s));
  end;
  if length(s) > 0 then sl.add(s);

  result := '';
  for i := 0 to sl.count - 1 do
  begin
    s := trim(sl[i]);
    if s = '' then continue;

    // if the path has got spaces in it, convert it to a short path.
    if pos(' ', s) > 0 then
    begin
      if directoryexists(s) then
        s := PathGetShortName(s);
    end;

    // drive letters in paths are pretty bad for linux, especially as the : is used as path delimiter...
    // so let's replace A:\-Z:\ and a:\-z:\ to /DRIVELETTER/
    for j := 65 to 90 do
    begin
      s := StringReplace(s, chr(j) + ':\', '/' + chr(j) + '/', [rfReplaceAll]);
    end;
    for j := 97 to 122 do
    begin
      s := StringReplace(s, chr(j) + ':\', '/' + chr(j) + '/', [rfReplaceAll]);
    end;

    // convert slashes
    s := StringReplace(s, '\', '/', [rfReplaceAll]);
    result := result + s;
    if i < sl.count - 1 then result := result + ':';
  end;
  sl.free;
end;

function getwindowspath(s: string): string;
begin
  if (length(s) > 3) and (s[1] = '/') and (s[3] = '/') then
  begin
    s[1] := s[2];
    s[2] := ':';
  end;
  s := StringReplace(s, '/', '\', [rfReplaceAll]);
  result := s;
end;

procedure TCrossFPCAddIn.docompile(build: boolean);
var
  i,j: integer;
  projectname, linuxprojectpath: string;

  commandline: string;
//  projectconfig: TStringlist;

  currentproject: IOTAProject;
//  outputstream: TMemoryStream;
//  outputfilename: string;

  SA: TSecurityAttributes;
  SI: TStartupInfo;
  PI: TProcessInformation;
  StdOutPipeRead, StdOutPipeWrite: THandle;
  WasOK: Boolean;
  Buffer: array[0..255] of Char;
  BytesRead: Cardinal;

  exefilename:string;

  projectscriptfilename:string;

  fname:string;
  efile:string;
  etmp:string;
  ekind:string;
  eline,ecol:integer;
  emsg:string;

  ProjectObject:TBESENObject;
  UtilsObject:TBESENObject;
  ResultValue:TBESENValue;
  InitValue:TBESENValue;
  DoneValue:TBESENValue;

  LastLineNumber:longint;
begin
  BESENInstance := nil;
  try

    CheckProjectFile;

    currentdir := getcurrentdir;
    currentproject := GetCurrentProject;
    if not assigned(currentproject) then exit;

    projectname := GetMainFilename(currentproject);

    projectpath := extractfilepath(projectname);
    if pos(' ', projectpath) > 0 then
    begin
      projectpath := PathGetShortName(projectpath);
    end;

    projectname := extractfilename(projectname);
    dolog('Project set to: ' + projectname);

    projectscriptfilename := ChangeFileExt(projectpath+projectname,'.cfs');
    if FileExists(projectscriptfilename) then
    begin
      BESENInstance := TBESEN.Create(COMPAT_BESEN);
      BESENInstance.RegisterNativeObject('File',TBESENObjectFile);

      ProjectObject := TBESENObject.Create(BESENInstance);
      ProjectObject.Put('path',BESENStringValue(projectpath),true);
      ProjectObject.Put('name',BESENStringValue(projectname),true);
      BESENInstance.ObjectGlobal.Put('project',BESENObjectValue(ProjectObject),true);

      UtilsObject := TBESENObjectUtils.Create(BESENInstance);
      UtilsObject.Construct(BESENObjectValue(UtilsObject),nil,0,ResultValue);
      BESENInstance.ObjectGlobal.Put('utils',ResultValue,true);

      InitValue := BESENNullValue;
      DoneValue := BESENNullValue;

      try
        BESENInstance.Execute(BESENConvertToUTF8(BESENGetFileContent(AnsiString(projectscriptfilename))));

        BESENInstance.ObjectGlobal.Get('init', InitValue);
        BESENInstance.ObjectGlobal.Get('done', DoneValue);

        if ((InitValue.ValueType=bvtOBJECT) and assigned(InitValue.Obj)) and (InitValue.Obj is TBESENObjectFunction) then begin
          cd.label_status.caption := 'Executing script "init" function...';
          try
            BESENInstance.ObjectCall(TBESENObjectFunction(InitValue.Obj),BESENNullValue,nil,0,ResultValue);
          except
            on e:Exception do begin
              LastLineNumber:=BESENInstance.LineNumber;
              if e is EBESENError then begin
                if LastLineNumber>0 then begin
                  MessageBoxW(Application.Handle,pwidechar('Line '+inttostr(LastLineNumber)+': '+BESENUTF8ToUTF16(AnsiString(e.Message))),pwidechar(EBESENError(e).Name),MB_OK or MB_ICONERROR);
                end else begin
                  MessageBoxW(Application.Handle,pwidechar(BESENUTF8ToUTF16(AnsiString(e.Message))),pwidechar(EBESENError(e).Name),MB_OK or MB_ICONERROR);
                end;
              end else begin
                if LastLineNumber>0 then begin
                  Application.MessageBox(pchar('Line '+inttostr(LastLineNumber)+': '+e.Message),'Exception',MB_OK or MB_ICONERROR);
                end else begin
                  Application.MessageBox(pchar(e.Message),'Exception',MB_OK or MB_ICONERROR);
                end;
              end;
              raise;
            end;
          end;
        end;
      except
        on e:Exception do begin
          InitValue := BESENNullValue;
          DoneValue := BESENNullValue;

          LastLineNumber:=BESENInstance.LineNumber;
          if e is EBESENError then begin
            if LastLineNumber>0 then begin
              MessageBoxW(Application.Handle,pwidechar('Line '+inttostr(LastLineNumber)+': '+BESENUTF8ToUTF16(AnsiString(e.Message))),pwidechar(EBESENError(e).Name),MB_OK or MB_ICONERROR);
            end else begin
              MessageBoxW(Application.Handle,pwidechar(BESENUTF8ToUTF16(AnsiString(e.Message))),pwidechar(EBESENError(e).Name),MB_OK or MB_ICONERROR);
            end;
          end else begin
            if LastLineNumber>0 then begin
              Application.MessageBox(pchar('Line '+inttostr(LastLineNumber)+': '+e.Message),'Exception',MB_OK or MB_ICONERROR);
            end else begin
              Application.MessageBox(pchar(e.Message),'Exception',MB_OK or MB_ICONERROR);
            end;
          end;
          raise;
        end;
      end;

    end;

{$ifndef standalonecrossfpc}
    currentproject.save(false, false);
{$endif}

    linuxprojectpath := getlinuxpath(projectpath);

{$ifndef standalonecrossfpc}
    (BorlandIDEServices as IOTAMessageServices).ClearCompilerMessages;
{$endif}

    ShowMessageView(false);
    cd.okButton.enabled := false;
    cd.label_project.caption := MinimizeName(projectpath + projectname, cd.label_project.Canvas,cd.label_project.Width);
    cd.label_status.caption := '';
    cd.label_status.font.Style := [];
    cd.label_errors.Caption := '0';
    cd.label_hints.caption := '0';
    cd.label_warnings.caption := '0';
    cd.Show;

    w_count := 0;
    e_count := 0;
    h_count := 0;
    l_count := 0;
    cc := 0;

    dolog('Target is '+FPCTargets[FPCTarget].FriendlyName);

  //  Commandline:=basedir+'bin\i386-win32\fpc.exe';
  //  Commandline:=basedir+FPCTargets[FPCTarget].CommandLine;
    CommandLine:='-n -vb '+QuoteStr('-FD'+basedir+FPCTargets[FPCTarget].UtilDir);
    if debugoutput then CommandLine:=CommandLine+' -va';
    if build then CommandLine:=CommandLine+' -B';

    CommandLine:=CombineFlags(CommandLine,
                              CombineFlags(DirectoryOptionsToString(Options),
                                           FPCTargets[FPCTarget].Parameters));
    CommandLine:=CombineFlags(CommandLine, ProjectOptions.GetConfigString);

  	CommandLine:=CombineFlags(CommandLine, readandconvertprojectcfg(projectpath + projectname));

    commandline := basedir+FPCTargets[FPCTarget].CommandLine + ' ' + CommandLine +' '+projectpath + projectname;

    if FPCTargets[FPCTarget].ShortName='linux-armv7a-softfloat' then
    begin
      // Small correct-paths workaround
      commandline := StringReplace(commandline,'$FPCTARGET','armv7a-softfloat-linux',[rfReplaceAll,rfIgnoreCase]);
    end;

    if build then
    begin
      dolog('Building ' + projectname + '...');
    end
    else
    begin
      dolog('Compiling ' + projectname + '...');
    end;
    dolog('Commandline: ' + commandline);
    //if debugoutput then dolog('Commandline: ' + commandline);


    with SA do
    begin
      nLength := SizeOf(SA);
      bInheritHandle := True;
      lpSecurityDescriptor := nil;
    end;
    {create pipe for standard output redirection}
    CreatePipe(StdOutPipeRead, {read handle}
      StdOutPipeWrite, {write handle}
      @SA, {security attributes}
      0 {number of bytes reserved for pipe - 0 default}
      );

    try
      {Make child process use StdOutPipeWrite as standard out, and
      make sure it does not show on screen}
      with SI do
      begin
        FillChar(SI, SizeOf(SI), 0);
        cb := SizeOf(SI);
        dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
        wShowWindow := SW_HIDE;
        hStdInput := GetStdHandle(STD_INPUT_HANDLE); {don't redirect stdinput}
        hStdOutput := StdOutPipeWrite;
        hStdError := StdOutPipeWrite;
      end;

      try
        {Make child process use StdOutPipeWrite as standard out, and
        make sure it does not show on screen}
        WasOK := CreateProcess(nil, PChar(CommandLine), nil, nil, True,
        NORMAL_PRIORITY_CLASS , // <-----
        nil, pchar(projectpath), SI, PI);
      finally
        {Close all remaining handles}
        CloseHandle(PI.hThread);
        CloseHandle(PI.hProcess);
      end;

      {Now that the handle has been inherited, close write to be safe. We don't
      want to read or write to it accidentally}
      CloseHandle(StdOutPipeWrite);
      {if process could be created then handle its output}

      application.processmessages;

      if not WasOK then
      begin
        dolog('INTERNAL ERROR: Could not execute command line!')
      end
      else
      try
        {get all output until DOS app finishes}
        Line := '';
        repeat
          {read block of characters (might contain carriage returns and line feeds)}
          WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
          {has anything been read?}
          if BytesRead > 0 then
          begin
            Application.processmessages;
            
            {finish buffer to PChar}
            Buffer[BytesRead] := #0;
            {combine the buffer with the rest of the last run}
            Line := Line + Buffer;
            //            dolog('Line: '+line);
            while (pos(#13, line) > 0) or (pos(#10, line) > 0) do
            begin
              // check for CRLF, CR, LF
              i := pos(#13#10, line);
              j := pos(#13, line);
              if (i = 0) or ((j > 0) and (j < i)) then i := j;
              j := pos(#10, line);
              if (i = 0) or ((j > 0) and (j < i)) then i := j;

              if i > 0 then
              begin
{$ifdef standalonecrossfpc}
                    if assigned(MessagesMemo) then
                    begin
                      if length(trim(Line))>0 then
                      begin
                        MessagesMemo.Lines.BeginUpdate;
                        try
                          MessagesMemo.Lines.Add(trim(line));
                        finally
                          MessagesMemo.Lines.EndUpdate;
                        end;
                        application.processmessages;
                        MessagesMemo.SelStart:=Length(MessagesMemo.Text);
                        MessagesMemo.SelLength:=0;
                        SendMessage(MessagesMemo.Handle, EM_SCROLLCARET, 0, 0);
                      end;
                    end;
{$endif}
                fname := trim(copy(line, 1, i));
                delete(line, 1, i);
                if fname <> '' then
                begin
                  if debugoutput then dolog('DEBUG: ' + fname);

                  if (pos(' Fatal: ', fname) > 0) or (pos(' Error: ', fname) > 0) or (pos(' Warning: ', fname) > 0) or (pos(' Hint: ', fname) > 0) or (pos(' Note: ', fname) > 0) then
                  begin
                    efile := copy(fname, 1, pos('(', fname) - 1);
                    if (length(efile)>2) and (efile[2]<>':') then efile:=IncludeTrailingPathDelimiter(currentdir)+efile;
                    delete(fname, 1, pos('(', fname));
                    etmp := copy(fname, 1, pos(')', fname) - 1);
                    delete(fname, 1, pos(')', fname) + 1);
                    if pos(',', etmp) > 0 then
                    begin
                      eline := strtointdef(copy(etmp, 1, pos(',', etmp) - 1), -1);
                      delete(etmp, 1, pos(',', etmp));
                      ecol := strtointdef(etmp, 1);
                    end
                    else
                    begin
                      eline := strtointdef(etmp, -1);
                      ecol := 1;
                    end;
                    ekind := copy(fname, 1, pos(':', fname) - 1);
                    if ekind = 'Error' then
                    begin
                      e_count := e_count + 1;
                      cd.label_errors.Caption := inttostr(e_count);
                    end
                    else if ekind = 'Fatal' then
                    begin
                      e_count := e_count + 1;
                      cd.label_errors.Caption := inttostr(e_count);
                    end
                    else if (ekind = 'Hint') or (ekind = 'Note') then
                    begin
                      h_count := h_count + 1;
                      cd.label_hints.Caption := inttostr(h_count);
                    end
                    else if ekind = 'Warning' then
                    begin
                      w_count := w_count + 1;
                      cd.label_warnings.Caption := inttostr(w_count);
                    end;

                    delete(fname, 1, pos(':', fname) + 1);
                    emsg := fname;
{$ifdef standalonecrossfpc}
{                   if assigned(MessagesMemo) then
                    begin
                      MessagesMemo.Lines.BeginUpdate;
                      try
                        MessagesMemo.Lines.Add('['+ekind+'] '+efile+'('+inttostr(eline)+','+inttostr(ecol)+'): '+emsg);
                      finally
                       MessagesMemo.Lines.EndUpdate;
                      end;
                      application.processmessages;
                      MessagesMemo.SelStart:=Length(MessagesMemo.Text);
                      MessagesMemo.SelLength:=0;
                      SendMessage(MessagesMemo.Handle, EM_SCROLLCARET, 0, 0);
                    end;}
{$else}
                    (BorlandIDEServices as IOTAMessageServices).AddToolMessage(efile, emsg, ekind, eline, ecol);
{$endif}
                    application.processmessages;
                  end
                  else
                  begin
                    if (pos('.pas', fname) > 0) or (pos('.dpr', fname) > 0) or (pos('.dpk', fname) > 0) then
                    begin
                      efile := copy(fname, 1, pos('(', fname) - 1);
                      while pos('/', efile) > 0 do
                        delete(efile, 1, 1);
                      delete(fname, 1, pos('(', fname));
                      etmp := copy(fname, 1, pos(')', fname) - 1);
                      if currentfile <> efile then
                      begin
                        l_count := l_count + cc;
                        cd.label_total.caption := inttostr(l_count);
                      end;

                      cc := strtointdef(etmp, 0);

                      cd.label_line.caption := inttostr(cc);
                      if currentfile <> efile then
                      begin
                        currentfile := efile;
                        cd.label_status.caption := 'Compiling:' + #9 + efile;
                      end;
                    end
                    else
                    if (pos('Fatal',fname)>0) or (pos('Error',fname)>0) or (pos('Warning',fname)>0) then
                    begin
                      if (pos('Fatal',fname)>0) or (pos('Error',fname)>0) then
                      begin
                        e_count:=e_count+1;
                        cd.label_errors.Caption := inttostr(e_count);
                        dolog(fname);
                      end
                      else if (pos('Warning',fname)>0) and (pos('libX11.so.6',fname)=0) and (pos('cause a linking failure', fname)=0) then
                      begin // ignore missing library warnings, it doesn't matter.
                        w_count:=w_count+1;
                        cd.label_warnings.Caption := inttostr(w_count);
                        dolog(fname);
                      end
                    end;
                  end;
                end;
              end;
            end;
          end
          else
          begin
            sleep(100);
            application.processmessages;
          end;  
        until
          not WasOK; // or (BytesRead = 0);
        {wait for console app to finish (should be already at this point)}
        if debugoutput then dolog('Finished...');
        l_count := l_count + cc;
        cd.label_total.caption := inttostr(l_count);

        Application.processmessages;

        cd.label_status.font.Style := [fsbold];
        if e_count > 0 then
          cd.label_status.caption := 'Done:' + #9 + 'There are errors.'
        else if w_count > 0 then
          cd.label_status.caption := 'Done:' + #9 + 'There are warnings.'
        else if h_count > 0 then
          cd.label_status.caption := 'Done:' + #9 + 'There are hints.'
        else
          cd.label_status.caption := 'Done.';
        WaitForSingleObject(PI.hProcess, 60 * 1000);
      finally
        {Close all remaining handles}
        CloseHandle(PI.hThread);
        CloseHandle(PI.hProcess);
      end;
    finally
      CloseHandle(StdOutPipeRead);
    end;

    Application.processmessages;


    l_count := l_count + cc;

    cd.label_total.caption := inttostr(l_count);
    cd.label_status.font.Style := [fsbold];

    if assigned(BESENInstance) and (((DoneValue.ValueType=bvtOBJECT) and assigned(DoneValue.Obj)) and (DoneValue.Obj is TBESENObjectFunction)) then begin
      cd.label_status.caption := 'Executing script "done" function...';
      try
        BESENInstance.ObjectCall(TBESENObjectFunction(DoneValue.Obj),BESENNullValue,nil,0,ResultValue);
      except
        on e:Exception do begin
          LastLineNumber:=BESENInstance.LineNumber;
          if e is EBESENError then begin
            if LastLineNumber>0 then begin
              MessageBoxW(Application.Handle,pwidechar('Line '+inttostr(LastLineNumber)+': '+BESENUTF8ToUTF16(AnsiString(e.Message))),pwidechar(EBESENError(e).Name),MB_OK or MB_ICONERROR);
            end else begin
              MessageBoxW(Application.Handle,pwidechar(BESENUTF8ToUTF16(AnsiString(e.Message))),pwidechar(EBESENError(e).Name),MB_OK or MB_ICONERROR);
            end;
          end else begin
            if LastLineNumber>0 then begin
              Application.MessageBox(pchar('Line '+inttostr(LastLineNumber)+': '+e.Message),'Exception',MB_OK or MB_ICONERROR);
            end else begin
              Application.MessageBox(pchar(e.Message),'Exception',MB_OK or MB_ICONERROR);
            end;
          end;
          raise;
        end;
      end;
    end;

    if e_count > 0 then
      cd.label_status.caption := 'Done:' + #9 + 'There are errors.'
    else if w_count > 0 then
      cd.label_status.caption := 'Done:' + #9 + 'There are warnings.'
    else if h_count > 0 then
      cd.label_status.caption := 'Done:' + #9 + 'There are hints.'
    else
      cd.label_status.caption := 'Done.';

    exefilename:=projectpath+projectname;
    delete(exefilename,pos(extractfileext(exefilename),exefilename),50);
    if (e_count = 0) and fileexists(exefilename) then dolog('Created executable: '+exefilename);

    ShowMessageView(true);
    cd.okButton.enabled := true;
  finally
    FreeAndNil(BESENInstance);
  end;
end;


(*
procedure TCrossFPCAddIn.CancelCompile(Sender: TObject);
begin
  cd.okButton.caption:='OK';
end;
*)

procedure dolog(const msg: string; refresh:boolean=true);
begin
{$ifndef standalonecrossfpc}
  (BorlandIDEServices as IOTAMessageServices).AddToolMessage('', msg, 'CrossFPC', -1, -1);
{$endif}
   if refresh then application.processmessages;
end;

procedure TCrossFPCAddIn.showoptions(Sender: TObject);
var oldbasedir:string;
begin
  CheckProjectFile;
  od.debugcheck.checked := DebugOutput;
  od.global:=@Options;
  od.Options:=Self.ProjectOptions;
  oldbasedir:=basedir;
  if basedir <> '' then
    od.install_folder.text := basedir;
  od.install_folderChange(self);
  if od.showmodal = mrOk then
  begin
    basedir := IncludeTrailingPathDelimiter(od.install_folder.text);
    if pos(' ', basedir) > 0 then
    begin
      basedir := PathGetShortName(basedir);
    end;
    DebugOutput := od.debugcheck.checked;
    if basedir<>oldbasedir then
    begin
//     Options.UnitSearchPath:=basedir+'units\$FPCTARGET\*;'+basedir+'lib\$FPCTARGET';
    end;
    SaveOptions;
  end;
end;

(*
procedure TCrossFPCAddIn.msgoutput(const s: string);
var
  i, j: integer;
  fname, efile, etmp: string;
  eline, ecol: integer;
  ekind: string;
  emsg: string;
begin
  Line := Line + s;
  while (pos(#13, line) > 0) or (pos(#10, line) > 0) do
  begin
    // check for CRLF, CR, LF
    i := pos(#13#10, line);
    j := pos(#13, line);
    if (i = 0) or ((j > 0) and (j < i)) then i := j;
    j := pos(#10, line);
    if (i = 0) or ((j > 0) and (j < i)) then i := j;

    if i > 0 then
    begin
      fname := trim(copy(line, 1, i));
      delete(line, 1, i);
      if fname <> '' then
      begin
        if debugoutput then dolog('DEBUG: ' + trim(fname),false);
        if (pos(' Fatal: ', fname) > 0) or (pos(' Error: ', fname) > 0) or (pos(' Warning: ', fname) > 0) or (pos(' Hint: ', fname) > 0) then
        begin
          efile := copy(fname, 1, pos('(', fname) - 1);
          efile := getwindowspath(efile);
          if (pos(':\', efile) < 2) then efile := projectpath + efile;

          delete(fname, 1, pos('(', fname));
          etmp := copy(fname, 1, pos(')', fname) - 1);
          delete(fname, 1, pos(')', fname) + 1);
          if pos(',', etmp) > 0 then
          begin
            eline := strtointdef(copy(etmp, 1, pos(',', etmp) - 1), -1);
            delete(etmp, 1, pos(',', etmp));
            ecol := strtointdef(etmp, 1);
          end
          else
          begin
            eline := strtointdef(etmp, -1);
            ecol := 1;
          end;
          ekind := copy(fname, 1, pos(':', fname) - 1);
          if ekind = 'Error' then
          begin
            e_count := e_count + 1;
            cd.label_errors.Caption := inttostr(e_count);
          end
          else if ekind = 'Fatal' then
          begin
            e_count := e_count + 1;
            cd.label_errors.Caption := inttostr(e_count);
          end
          else if ekind = 'Hint' then
          begin
            h_count := h_count + 1;
            cd.label_hints.Caption := inttostr(h_count);
          end
          else if ekind = 'Warning' then
          begin
            w_count := w_count + 1;
            cd.label_warnings.Caption := inttostr(w_count);
          end;

          delete(fname, 1, pos(':', fname) + 1);
          emsg := fname;
          (BorlandIDEServices as IOTAMessageServices).AddToolMessage(efile, emsg, ekind, eline, ecol);
          application.processmessages;
        end
        else
        begin
          if (pos('.pas', fname) > 0) or (pos('.dpr', fname) > 0) or (pos('.dpk', fname) > 0) then
          begin
            efile := copy(fname, 1, pos('(', fname) - 1);
            while pos('/', efile) > 0 do
              delete(efile, 1, 1);
            delete(fname, 1, pos('(', fname));
            etmp := copy(fname, 1, pos(')', fname) - 1);
            if currentfile <> efile then
            begin
              l_count := l_count + cc;
              cd.label_total.caption := inttostr(l_count);
            end;

            cc := strtointdef(etmp, 0);

            cd.label_line.caption := inttostr(cc);
            if currentfile <> efile then
            begin
              currentfile := efile;
              cd.label_status.caption := 'Compiling:' + #9 + efile;
            end;
          end
          else if (pos('Fatal', fname) > 0) or (pos('Error', fname) > 0) or (pos('Warning', fname) > 0) then
          begin
            if (pos('Fatal', fname) > 0) or (pos('Error', fname) > 0) then
            begin
              e_count := e_count + 1;
              cd.label_errors.Caption := inttostr(e_count);
              dolog(fname);
            end
            else if (pos('Warning', fname) > 0) and (pos('libX11.so.6', fname) = 0) then
            begin // ignore libX11.so.6 warning, it doesn't matter.
              w_count := w_count + 1;
              cd.label_warnings.Caption := inttostr(w_count);
              dolog(fname);
            end
          end;
        end;
      end;
    end;
  end;
end;   *)

{ TCrossFPCNotifier }

{$ifndef standalonecrossfpc}
procedure TCrossFPCNotifier.AfterCompile(Succeeded: Boolean);
begin
  //
end;

procedure TCrossFPCNotifier.BeforeCompile(const Project: IOTAProject;
  var Cancel: Boolean);
begin
  //
end;

procedure TCrossFPCNotifier.FileNotification(
  NotifyCode: TOTAFileNotification; const FileName: string;
  var Cancel: Boolean);
var currentproject: IOTAProject;
begin
  case NotifyCode of
    ofnFileOpened, ofnFileClosing, ofnActiveProjectChanged:
    begin
      currentproject:=GetCurrentProject;
      if assigned(currentproject) and (GetMainFilename(GetCurrentProject)<>'') {$IFDEF VER180}and (pos('Delphi',currentproject.Personality)>0){$ENDIF} then
      begin
        CrossFPCAddIn.CompileMenuItem.enabled:=true;
        CrossFPCAddIn.BuildMenuItem.enabled:=true;
        CrossFPCAddIn.DebugMenuItem.enabled:=true;
      end
      else
      begin
        CrossFPCAddIn.CompileMenuItem.enabled:=false;
        CrossFPCAddIn.BuildMenuItem.enabled:=false;
        CrossFPCAddIn.DebugMenuItem.enabled:=false;
      end;
    end;
  end;
end;
{$endif}

procedure TCrossFPCAddIn.LoadOptions;
var i: Integer;
    DoWriteBack : boolean;
begin
  DoWriteBack := false;

{ basedir := ini.ReadString('CrossFPC', 'BaseDir', '');
  if basedir <> '' then basedir := IncludeTrailingPathDelimiter(basedir);
  if pos(' ', basedir) > 0 then
  begin
    basedir := PathGetShortName(basedir);
  end;}

  TargetCount := ini.ReadInteger('CrossFPC','Targets',0);
  if TargetCount=0 then
  begin
    DoWriteBack := true;
    TargetCount:=7;
    SetLength(FPCTargets, TargetCount);
    with FPCTargets[0] do
    begin
      ShortName:='win-i386';
      FriendlyName:='Win32';
      CommandLine:='bin\i386-win32\fpc.exe';
      Parameters:='-Twin32';
      UtilDir:='bin\i386-win32';
    end;
    with FPCTargets[1] do
    begin
      ShortName:='win-amd64';
      FriendlyName:='Win64';
      CommandLine:='bin\i386-win32\ppcrossx64.exe';
      Parameters:='-Twin64';
      UtilDir:='bin\i386-win32';
    end;
    with FPCTargets[2] do
    begin
      ShortName:='linux-i386';
      FriendlyName:='Linux (i386)';
      CommandLine:='bin\i386-win32\fpc.exe';
      Parameters:='-Tlinux -XPi386-linux- '; // -Sk -k--allow-shlib-undefined
///   Parameters:='-Tlinux -FL/lib/ld-linux.so.2 -XPi386-linux- -Sk -k--allow-shlib-undefined';
      UtilDir:='bin\i386-linux';
    end;
    with FPCTargets[3] do
    begin
      ShortName:='linux-amd64';
      FriendlyName:='Linux (x64)';
      CommandLine:='bin\i386-win32\ppcrossx64.exe';
      Parameters:='-Tlinux -XPx86_64-linux- -Sk -k--allow-shlib-undefined';
//    Parameters:='-Tlinux -XPx86_64-linux- -FL/lib/ld-linux.so.2 -Sk -k--allow-shlib-undefined';
//    Parameters:='-Tlinux -FL/lib/ld-linux.so.2 -XPx86_64-linux- -Sk -k--allow-shlib-undefined';
      UtilDir:='bin\x86_64-linux';
    end;
    with FPCTargets[4] do
    begin
      ShortName:='linux-arm';
      FriendlyName:='Linux (arm)';
      CommandLine:='bin\i386-win32\ppcrossarm.exe';
      Parameters:='-Tlinux -XParm-linux- -CpARMV5 -CfSOFT -Sk -k--allow-shlib-undefined';
//    Parameters:='-Tlinux -FL/lib/ld-linux.so.2 -XParm-linux- -Sk -k--allow-shlib-undefined';
      UtilDir:='bin\arm-linux';
    end;
    with FPCTargets[5] do
    begin
      ShortName:='android-arm';
      FriendlyName:='Android (arm)';
      CommandLine:='bin\i386-win32\ppcrossarmandroid.exe';
      Parameters:='-Tandroid -XParm-linux-androideabi- -CpARMv7A -CfVFPv3 -k--allow-shlib-undefined';
      UtilDir:='bin\arm-android';
    end;
    with FPCTargets[6] do
    begin
      ShortName:='linux-armv7a-softfloat';
      FriendlyName:='Linux (armv7a-softfloat)';
      CommandLine:='bin\i386-win32\ppcrossarmv7a-softfloat-linux.exe';
      Parameters:='-Tlinux -XParm-linux- -CpARMv7a -CfSOFT -dCPUARMV7A -dLINUXARMV7ASOFTFLOAT -k--allow-shlib-undefined';
      UtilDir:='bin\armv7a-softfloat-linux';
    end;
  end else
  begin
    SetLength(FPCTargets, TargetCount);
    for i:=0 to TargetCount-1 do
    with FPCTargets[i] do
    begin
      ShortName:=ini.ReadString('Target'+IntToStr(i+1),'ShortName','Unknown');
      FriendlyName:=ini.ReadString('Target'+IntToStr(i+1),'FriendlyName','Unknown');
      CommandLine:=ini.ReadString('Target'+IntToStr(i+1), 'CommandLine','bin\i386-win32\fpc.exe');
      Parameters:=ini.ReadString('Target'+IntToStr(i+1), 'Parameters','-Twin32');
      UtilDir:=ini.ReadString('Target'+IntToStr(i+1),'UtilDir','i386-win32');
      MenuItem:=nil;
    end;
  end;

  debugoutput := ini.ReadBool('CrossFPC', 'DebugOutput', false);
  with Options do
  begin
    if basedir='' then
    UnitDir := ini.ReadString('CrossFPC', 'UnitDirectories', '') else
    UnitDir := ini.ReadString('CrossFPC', 'UnitDirectories', basedir+'units\$FPCTARGET\*;'+basedir+'lib\$FPCTARGET');
    IncludeDir := ini.ReadString('CrossFPC', 'IncludeDirectories', '');
    LibraryDir := ini.ReadString('CrossFPC', 'ObjectDirectories', '');

    // InputCodePage := ini.ReadString('CrossFPC', 'Encoding', '');
    // OutputDir := ini.ReadString('CrossFPC', 'OutputDirectory', '');
    // CompilerMode:=cmDelphi;
//    ExecutableOptions:=[eoNoStandardPath, eoStripSymbols, eoSmartLink];
//    ExecutableOptions:=[eoNoStandardPath, eoSmartLink];
//    CodeOptions:=[coCreateSmartlink];
//    SyntaxOptions:=[soAllowGoto, soAnsiStrings];
//    TargetOS:=toLinux;
//    OutputFormat:=ofElf;
//    CrossLinkLibPrefix:='i386-linux-';
//    DynamicLinker:='/lib/ld-linux.so.2';
  end;

  if DoWriteBack then
  begin
    ini.WriteInteger('CrossFPC','Targets',TargetCount);
    for i:=0 to TargetCount-1 do
    With FPCTargets[i] do
    begin
      ini.WriteString('Target'+IntToStr(i+1), 'ShortName', ShortName);
      ini.WriteString('Target'+IntToStr(i+1), 'FriendlyName', FriendlyName);
      ini.WriteString('Target'+IntToStr(i+1), 'CommandLine', CommandLine);
      ini.WriteString('Target'+IntToStr(i+1), 'Parameters', Parameters);
      ini.WriteString('Target'+IntToStr(i+1), 'UtilDir', UtilDir);
    end;
    ini.UpdateFile;
  end;
end;

procedure TCrossFPCAddIn.SaveOptions;
var i: Integer;
begin
  ini.WriteString('CrossFPC', 'BaseDir', basedir);
  ini.WriteString('CrossFPC', 'UnitDirectories', Options.UnitDir);
  ini.WriteString('CrossFPC', 'ObjectDirectories', Options.LibraryDir);
  ini.WriteString('CrossFPC', 'IncludeDirectories', Options.IncludeDir);
//  ini.WriteString('CrossFPC', 'OutputDirectory', Options.OutputDir);
  ini.WriteString('CrossFPC', 'Encoding', Encoding);
  ini.WriteBool('CrossFPC', 'DebugOutput', Debugoutput);
  ini.WriteInteger('CrossFPC','Targets',TargetCount);
  for i:=0 to TargetCount-1 do
  With FPCTargets[i] do
  begin
    ini.WriteString('Target'+IntToStr(i+1), 'ShortName', ShortName);
    ini.WriteString('Target'+IntToStr(i+1), 'FriendlyName', FriendlyName);
    ini.WriteString('Target'+IntToStr(i+1), 'CommandLine', CommandLine);
    ini.WriteString('Target'+IntToStr(i+1), 'Parameters', Parameters);
    ini.WriteString('Target'+IntToStr(i+1), 'UtilDir', UtilDir);
  end;
  ini.UpdateFile;
end;

procedure TCrossFPCAddIn.SetTarget(Sender: TObject);
var i: Integer;
begin
  FPCTarget:=(Sender as TComponent).Tag;
  if FPCTargets[FPCTarget].FriendlyName='reload' then
  begin
    FPCTarget:=0;
    for i:=0 to TargetCount-1 do
    with FPCTargets[i] do
    begin
      FriendlyName:=ini.ReadString('Target'+IntToStr(i+1),'FriendlyName','unknown');
      CommandLine:=ini.ReadString('Target'+IntToStr(i+1), 'CommandLine','');
      UtilDir:=ini.ReadString('Target'+IntToStr(i+1),'UtilDir','i386-win32');
    end;
  end;
  for i:=0 to TargetCount-1 do
  FPCTargets[i].MenuItem.Checked := i=FPCTarget;
end;

procedure TCrossFPCAddIn.CheckProjectFile;
begin
  if GetCurrentProject<>nil then
  ProjectOptions.ProjectFile:=GetMainFilename(GetCurrentProject)
  else ProjectOptions.ProjectFile:='';
end;

end.

